﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration;
using System.Configuration.Provider;
using System.Data.Odbc;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Web.Security;
using NHibernate;
using Peak.Core;
using Peak.Core.DataInterfaces;
using SharpArch.Core.PersistenceSupport;
using Microsoft.Practices.ServiceLocation;

namespace Peak.Web.Controllers.Providers
{
  public class CustomRoleProvider : RoleProvider
  {
    #region private
    private string eventSource = "CustomRoleProvider";
    private string eventLog = "Application";
    private string exceptionMessage = "An exception occurred. Please check the Event Log.";
    
    #endregion

    #region Properties
    public override string ApplicationName { get; set; }
    
    public ISession Session
    {
      get
      {
        return SharpArch.Data.NHibernate.NHibernateSession.Current;
      }
    }

    public IRoleRepository RoleRepository
    {
      get
      {
        return ServiceLocator.Current.GetInstance<IRoleRepository>();
      }
    }

    public bool WriteExceptionsToEventLog { get; set; }
    #endregion

    #region Helper Functions
    // A helper function to retrieve config values from the configuration file
    private string GetConfigValue(string configValue, string defaultValue)
    {
      if (String.IsNullOrEmpty(configValue))
        return defaultValue;

      return configValue;
    }

    private void WriteToEventLog(Exception e, string action)
    {
      EventLog log = new EventLog();
      log.Source = eventSource;
      log.Log = eventLog;

      string message = exceptionMessage + "\n\n";
      message += "Action: " + action + "\n\n";
      message += "Exception: " + e.ToString();

      log.WriteEntry(message);
    }
    #endregion

    #region Private Methods
    //get a role by name
    private Role GetRole(string name)
    {
      return RoleRepository.GetByName(name);
    }

    #endregion

    #region Public Methods
    //initializes the FNH role provider
    public override void Initialize(string name, NameValueCollection config)
    {
      // Initialize values from web.config.

      if (config == null)
        throw new ArgumentNullException("config");

      if (name == null || name.Length == 0)
        name = "FNHRoleProvider";

      if (String.IsNullOrEmpty(config["description"]))
      {
        config.Remove("description");
        config.Add("description", "Sample Fluent Nhibernate Role provider");
      }

      // Initialize the abstract base class.
      base.Initialize(name, config);

      WriteExceptionsToEventLog = Convert.ToBoolean(GetConfigValue(config["writeExceptionsToEventLog"], "true"));
    }

    //adds a user collection toa roles collection
    public override void AddUsersToRoles(string[] usernames, string[] Names)
    {
      User usr = null;
      foreach (string Name in Names)
      {
        if (!RoleExists(Name))
          throw new ProviderException(String.Format("Role name {0} not found.", Name));
      }

      foreach (string username in usernames)
      {
        if (username.Contains(","))
          throw new ArgumentException(String.Format("User names {0} cannot contain commas.", username));
        //is user not exiting //throw exception

        foreach (string Name in Names)
        {
          if (IsUserInRole(username, Name))
            throw new ProviderException(String.Format("User {0} is already in role {1}.", username, Name));
        }
      }

      using (ITransaction transaction = Session.BeginTransaction())
      {
        try
        {
          foreach (string username in usernames)
          {
            foreach (string Name in Names)
            {
              //get the user
              usr = Session.CreateCriteria(typeof(User))
                .Add(NHibernate.Criterion.Restrictions.Eq("Username", username))
                .Add(NHibernate.Criterion.Restrictions.Eq("ApplicationName", this.ApplicationName))
                .UniqueResult<User>();

              if (usr != null)
              {
                //get the role first from db
                Role role = Session.CreateCriteria(typeof(Role))
                  .Add(NHibernate.Criterion.Restrictions.Eq("Name", Name))
                  .Add(NHibernate.Criterion.Restrictions.Eq("ApplicationName", this.ApplicationName))
                  .UniqueResult<Role>();

                //Role role = GetRole(Name);
                usr.AddRole(role);
              }
            }
            Session.SaveOrUpdate(usr);
          }
          transaction.Commit();
        }
        catch (Exception e)
        {
          if (WriteExceptionsToEventLog)
            WriteToEventLog(e, "AddUsersToRoles");
          else
            throw e;
        }

      }
    }

    //create  a new role with a given name
    public override void CreateRole(string Name)
    {
      if (Name.Contains(","))
        throw new ArgumentException("Role names cannot contain commas.");

      if (RoleExists(Name))
        throw new ProviderException("Role name already exists.");
            
      Role role = new Role();
      role.Name = Name;

      RoleRepository.SaveOrUpdate(role);
    }

    //delete a role with given name
    public override bool DeleteRole(string name, bool throwOnPopulatedRole)
    {
      if (!RoleExists(name))
        throw new ProviderException("Role does not exist.");

      if (throwOnPopulatedRole && GetUsersInRole(name).Length > 0)
        throw new ProviderException("Cannot delete a populated role.");

      RoleRepository.Delete(GetRole(name));
      
      return true;
    }

    //get an array of all the roles
    public override string[] GetAllRoles()
    {
      return RoleRepository.GetAll().Select(f => f.Name).ToArray();
    }

    public override string[] GetRolesForUser(string userName)
    {
      return RoleRepository.GetByUserName(userName).Select(f=>f.Name).ToArray();      
    }

    public override string[] GetUsersInRole(string name)
    {
      return RoleRepository.GetByName(name).UsersInRole.Select(f => f.UserName).ToArray();      
    }

    //determine is a user has a given role
    public override bool IsUserInRole(string userName, string name)
    {
      return RoleRepository.IsUserInRole(userName, name);
    }

    public override void RemoveUsersFromRoles(string[] userNames, string[] names)
    {
      throw new NotImplementedException();
    }

    public override bool RoleExists(string name)
    {
      return RoleRepository.GetByName(name) != null;
    }

    //find users that beloeng to a particular role , given a username, Note : does not do a LIke search
    public override string[] FindUsersInRole(string Name, string usernameToMatch)
    {
      throw new NotImplementedException();
    }
    #endregion
  }
}
